package org.mozhu.mboot.core.web.sitemesh.filter;

import lombok.extern.slf4j.Slf4j;
import org.mozhu.mboot.core.json.JSON;
import org.mozhu.mboot.core.web.sitemesh.SiteMeshProperties;
import org.mozhu.mboot.core.web.sitemesh.builder.SpringMVCSiteMeshFilterBuilder;
import org.mozhu.mboot.core.web.sitemesh.tag.MetaTagDecoratorSelector;
import org.sitemesh.builder.SiteMeshFilterBuilder;
import org.sitemesh.config.ConfigurableSiteMeshFilter;
import org.sitemesh.config.xml.XmlFilterConfigurator;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.web.servlet.ViewResolver;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import javax.servlet.Filter;
import javax.servlet.ServletException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

@Slf4j
public class DefaultConfigurableSiteMeshFilter extends ConfigurableSiteMeshFilter {
    private ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

    private ApplicationContext applicationContext;
    private SiteMeshProperties properties;

    public DefaultConfigurableSiteMeshFilter(ApplicationContext applicationContext, SiteMeshProperties properties) {
        this.applicationContext = applicationContext;
        this.properties = properties;
    }

    @Override
    protected Filter setup() throws ServletException {
        ViewResolver viewResolver = applicationContext.getBean(properties.getViewResolver(), ViewResolver.class);
        SiteMeshFilterBuilder builder = new SpringMVCSiteMeshFilterBuilder(viewResolver);

        // should before load xml
        setupDecoratorSelector(builder);

        new XmlFilterConfigurator(getObjectFactory(),
                loadConfigXml(getConfigFileName()))
                .configureFilter(builder);

        applyCustomConfiguration(builder);

        return builder.create();
    }
    
    protected void setupDecoratorSelector(SiteMeshFilterBuilder builder) {
        JSON json = applicationContext.getBean(JSON.class);
        // force JSON utils to init
        Map<String, String> decoratorPaths = json.toMap(properties.getDecoratorPaths(), String.class, String.class);
        builder.setCustomDecoratorSelector(new MetaTagDecoratorSelector(decoratorPaths));
    }

    // I think there is no need to reload the config
    @Override
    protected boolean getAutoReload() {
        return false;
    }

    @Override
    protected boolean reloadRequired() {
        return false;
    }

    @Override
    protected String getConfigFileName() {
        return properties.getConfigFile();
    }

    protected Element loadConfigXml(String configFilePath) throws ServletException {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = factory.newDocumentBuilder();

            Resource xmlConfigResource = resolver.getResource(configFilePath);
            if (xmlConfigResource != null && xmlConfigResource.isReadable()) {
                InputStream stream = xmlConfigResource.getInputStream();
                if (stream == null) {
                    log.info("No config file present - using defaults and init-params. Tried: ServletContext:" + configFilePath);
                    return null;
                }
                try {
                    log.info("Loading SiteMesh 3 config file from Classpath: " + configFilePath);
                    Document document = documentBuilder.parse(stream);
                    return document.getDocumentElement();
                } catch (SAXException e) {
                    throw new ServletException("Could not parse " + configFilePath + " (loaded by ServletContext)", e);
                } finally {
                    stream.close();
                }
            }
            return null;
        } catch (IOException e) {
            throw new ServletException(e);
        } catch (ParserConfigurationException e) {
            throw new ServletException("Could not initialize DOM parser", e);
        }
    }
}
